import { useState, useEffect, useRef, useContext, createContext } from "react";
import { checkCollision } from "./utils";
import config from "./config";

/** 状态 */
export enum Status {
  start = "start",
  pause = "pause",
  reset = "reset",
  init = "init",
}

/** 键盘方向键 */
export enum Direction {
  down = "ArrowDown",
  up = "ArrowUp",
  right = "ArrowRight",
  left = "ArrowLeft",
}

/** 状态上下文 */
const StatusContext = createContext<Status | null>(null);

/** 状态更新上下文 */
const StatusActionContext = createContext<StatusAction | null>(null);

/** 速度上下文 */
const SpeedContext = createContext<number | null>(null);

/** 速度更新上下文 */
const SpeedActionContext = createContext<SpeedAction | null>(null);

/** 布局上下文 */
const LayoutContext = createContext<Layout | null>(null);

/** 检测碰撞上下文 */
const CheckCollisionContext = createContext<CollisionInfo | null>(null);

/** 使用状态信息 */
export function useStatus() {
  return useContext(StatusContext) as Status;
}

/** 使用状态更新方法 */
export function useStatusActions() {
  return useContext(StatusActionContext) as StatusAction;
}

/** 使用速度信息 */
export function useSpeed() {
  return useContext(SpeedContext) as number;
}

/** 使用速度信息 */
export function useSpeedAction() {
  return useContext(SpeedActionContext) as SpeedAction;
}

/** 使用布局信息 */
export function useLayout(): Layout {
  return useContext(LayoutContext) as Layout;
}

/** 使用检测碰撞信息 */
export function useCheckCollision() {
  return useContext(CheckCollisionContext) as CollisionInfo;
}

/** 获取检测碰撞信息 */
function getCollisionInfo(refValue: RefValue | null) {
  if (refValue === null) {
    return { collisionPoint: { x: 0, y: 0 }, isCollision: false };
  }

  const { snakeRef, foodRef } = refValue;
  const snakeHeadPoint = snakeRef.current!.getSnakeHeadPoint();
  const foodPoints = foodRef.current!.getFoodPoints();

  for (const foodPoint of foodPoints) {
    if (checkCollision(snakeHeadPoint, foodPoint)) {
      return { collisionPoint: snakeHeadPoint, isCollision: true };
    }
  }

  return { collisionPoint: snakeHeadPoint, isCollision: false };
}

/** 获取布局信息 */
function getLayoutInfo() {
  // 视口高度
  const windowH = window.innerHeight;

  // 计分板和按钮区占据的高度
  const diffValue = config.diffValue;

  // 舞台网格大小
  const gridSize = config.gridSize;

  // 舞台网格数量
  const gridCount = Math.trunc((windowH - diffValue) / gridSize);

  // 舞台大小
  const stageSize = gridSize * gridCount;

  return { gridCount, diffValue, gridSize, stageSize };
}

/** 上下文提供器 */
export function ContextProvider({ children, refValue }: ContextParams) {
  const [collisionInfo, setCollisionInfo] = useState<CollisionInfo>(() =>
    getCollisionInfo(null)
  );
  const [status, setStatus] = useState<Status>(Status.init);
  const [speed, setSpeed] = useState(config.speed);
  const layoutInfo = getLayoutInfo();
  const rafRef = useRef(0);

  const actions = {
    onStart: () => setStatus(Status.start),
    onPause: () => setStatus(Status.pause),
    onReset: () => setStatus(Status.reset),
  };

  useEffect(() => {
    if (status === Status.start) {
      rafRef.current = requestAnimationFrame(function RAF() {
        setCollisionInfo(() => getCollisionInfo(refValue));
        rafRef.current = requestAnimationFrame(RAF);
      });
    } else {
      cancelAnimationFrame(rafRef.current);
    }

    return () => cancelAnimationFrame(rafRef.current);
  }, [status]);

  return (
    <StatusContext.Provider value={status}>
      <StatusActionContext.Provider value={actions}>
        <SpeedContext.Provider value={speed}>
          <SpeedActionContext.Provider value={setSpeed}>
            <LayoutContext.Provider value={layoutInfo}>
              <CheckCollisionContext.Provider value={collisionInfo}>
                {children}
              </CheckCollisionContext.Provider>
            </LayoutContext.Provider>
          </SpeedActionContext.Provider>
        </SpeedContext.Provider>
      </StatusActionContext.Provider>
    </StatusContext.Provider>
  );
}
